Skip to content

Change Flamegraph handling of narrow boxes (snap to multiples of 2 device pixels)#4601

Merged
mstange merged 3 commits into
firefox-devtools:mainfrom
mstange:flamegraph-snapping
May 9, 2023
Merged

Change Flamegraph handling of narrow boxes (snap to multiples of 2 device pixels)#4601
mstange merged 3 commits into
firefox-devtools:mainfrom
mstange:flamegraph-snapping

Conversation

@mstange

@mstange mstange commented May 1, 2023

Copy link
Copy Markdown
Contributor

Fixes #4591.

Production | Deploy preview

I'm also changing some of the other charts for consistency. Please review each commit individually.

The last commit has a snap function which says something about moving boxes past zero. This consistent snapping isn't really needed for the flame graph, but it could become relevant for the stack chart or the marker chart if we ever reuse this code there in the future.

@mstange mstange requested a review from julienw May 1, 2023 21:10
@mstange mstange self-assigned this May 1, 2023

@julienw julienw left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the change! This looks definitely better than the current rendering where a lot of things are missing.

I left a few comments but I'm mostly OK landing this as is and improving this further later.

Comment on lines +43 to +48
export type ChartCanvasScale = {
// Always equal to devicePixelRatio
cssToDeviceScale: number,
// 1 if scaleCtxToCssPixels is true, otherwise equal to cssToDeviceScale
cssToUserScale: number,
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it useful to have both of them? I understand the rationale but I see it's used just to compare both to detect errors. Maybe just cssToUserScale is useful, because that's the one that actually changes when scaleCtxToCssPixels changes? But also scaleCtxToCssPixels is set by the caller anyway so the caller knows about that.
All in all I'm not convinced by adding this structure compared to just keep using devicePixelRatio directly. But I'm not against either so we can keep it :-)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm undecided. Their "usefulness" comes into play when somebody reads the drawFilter method without having read the rest of the file; you can see at the start of the method which space you're rendering into.

) {
this._textMeasurement = new TextMeasurement(ctx);
this._textMeasurementCssToDeviceScale = cssToDeviceScale;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

optional suggestion: use memoize-one with a function taking cssToDeviceScale as parameter. This would move the logic to invalidate the text measurement tool out of this function.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also this could possibly be handled directly by the shared Canvas code, passed to drawCanvas with the other properties. Because it looks like that the same code is repeated in all these charts.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, these suggestions would improve things! I'll leave them for follow-ups though.

Comment thread src/components/flame-graph/Canvas.js Outdated
Comment on lines +179 to +208
const { cssToUserScale } = scale;
if (cssToUserScale !== 1) {
throw new Error(
'FlameGraphCanvasImpl sets scaleCtxToCssPixels={true}, so canvas user space units should be equal to CSS pixels.'
);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these checks useful? After all, this component controls the prop scaleCtxToCssPixels, so is there a risk that in the future this might break?

(removing them is optional, I'm defering to your opinion, but I'm just curious about your thoughts)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm less convinced that they're needed now, compared to when I was first reading this code. In the beginning I found it very counter-intuitive that drawCanvas would need to render into a completely different space depending on the value of a prop that was passed at the other end of the file. I wanted something locally inside drawCanvas that would let me orient myself (let me know which space I'm rendering into).
Now that I've looked at this code a bunch, I'm less convinced that this is such a big deal. But I'll keep it anyway. I think if we end up switching all charts to render in device pixels, we could remove these assertions and the cssToUserScale argument.

Comment on lines +267 to +269
// For each box, snap the left and right edges to the nearest multiple
// of two device pixels. If both edges snap to the same value, the box
// becomes empty and is not drawn.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure of this. I think we should snap to at least 1px if there's a box, but do it just for one such small box on this px (if there are more than one box here), and only if there's no bigger box at this location.
This would be similar to (but not completely the same) what we do in the marker chart.
What do you think?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, when selecting a preview while viewing the flame graph on your deploy preview, we see "peaks" showing up and then disappearing very often. I don't know if this is the reason, but I'd expect the peaks to still show up even if they have a small width. It would be great if the view would stay mostly stable.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's true, both of these aspects are "cons" to the "just snap" approach. But I think they're outweighed by the "pros": The algorithm is simple and fast, and it never produces incorrect nesting: If box A is rendered below box B (in the same pixel column), you know that A is the caller of B. It seems hard to preserve peaks while also preserving correct nesting.

For what it's worth, Chrome's devtools performance panel also has flickering peaks as you zoom in and out.

mstange added 3 commits May 9, 2023 11:21
In the past, the components which draw in device space were using
window.devicePixelRatio and relying on the assumption that this is the
same scale that ChartCanvas was using when it set the canvas backing
size and CSS size. This assumption is correct, but it still feels better
to pass the scale along explicitly, rather than assuming something about
the global state.

I'm also adding some checks so that it's obvious to readers of the
drawCanvas methods how the canvas scale is controlled.
Fixes firefox-devtools#4591.

This changes how small boxes and gaps are handled. If a row consists of
many adjacent narrow boxes, we will now fill that entire row with
painted pixels. Before this patch, we would skip all narrow boxes and
leave large parts of the rendering empty.
@mstange mstange force-pushed the flamegraph-snapping branch from 73c8658 to 2bf992e Compare May 9, 2023 15:21
@mstange mstange enabled auto-merge May 9, 2023 15:22
@mstange mstange merged commit f7185ec into firefox-devtools:main May 9, 2023
@canova canova mentioned this pull request May 30, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Flamegraph doesn't render thin adjacent boxes

2 participants